package
{
	import com.adobe.utils.AGALMiniAssembler;
	
	import flash.display.Sprite;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Matrix3D;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Timer;
	import flash.utils.getTimer;
	
	[SWF(width="1000", height="1000", frameRate="60", backgroundColor="#000000")]
	public class Ocean3D extends Sprite
	{
		protected var context3D:Context3D;
		protected var program:Program3D;
		protected var vertexbuffer:VertexBuffer3D;
		protected var indexbuffer:IndexBuffer3D;
		public var timer:Timer = new Timer(1000);
		public var vertices:Vector.<Number>;
		public var loader:URLLoader = new URLLoader();
		public var colorNone:Array = [0xbd, 0xbd, 0xbd];
		public var colors:Array = [ [0x56, 0xf5, 0xd8], [0x00, 0xec, 0xad], [0x75, 0xf0, 0x38],
			[0xcf, 0xff, 0x00], [0xff, 0xff, 0x00], [0xff, 0xea, 0x50], [0xff, 0xcd, 0x50], [0xff, 0xa2, 0x50],
			[0xff, 0x7f, 0x50], [0xff, 0x63, 0x47], [0xff, 0x00, 0x00] ];		
		public var scales:Array = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, int.MAX_VALUE];
		public var cellWidth:int = 229;
		public var cellHeight:int = 169;
		public var data:ByteArray;
		public var _vertices:Vector.<Number>;
		public var _indices:Vector.<uint>;
		
		public function Ocean3D()
		{			
			stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, loadData);
			stage.stage3Ds[0].requestContext3D();
			
//			addEventListener(Event.ENTER_FRAME, onRender);
		}
		
		public function loadData(e:Event):void {
			loader.dataFormat = URLLoaderDataFormat.BINARY;
			loader.addEventListener(Event.COMPLETE, initMolehill);
			loader.load(new URLRequest("E:/work/flash/Ocean/data"));
		}
		
		public function initVerices():void {
			_vertices = new Vector.<Number>(cellWidth*cellHeight*6);
			var index:int = 0;
			var i:int; var j:int;
			var tx:Number;var ty:Number;
			for (j = 0; j < cellHeight; j++) {
				for (i = 0; i < cellWidth; i++) {
					_vertices[index++] = Number(i)*2/500;
					_vertices[index++] = Number(j)*2/500;
					_vertices[index++] = 0;
					_vertices[index++] = 0;
					_vertices[index++] = 1;
					_vertices[index++] = 0;
				}
			}
			_indices = new Vector.<uint>((cellWidth-1)*(cellHeight-1)*2*3);
			index = 0;
			for (j = 0; j < cellHeight-1; j++) {
				for (i = 0; i < cellWidth-1; i++) {
					_indices[index++] = pointIndex(i, j);
					_indices[index++] = pointIndex(i+1, j);
					_indices[index++] = pointIndex(i, j+1);
					_indices[index++] = pointIndex(i+1, j+1);
					_indices[index++] = pointIndex(i, j+1);
					_indices[index++] = pointIndex(i+1, j);
					
				}
			}
		}
		
		public function pointIndex(x:int, y:int):int {
			return y * cellWidth + x;
		}
		
		public function showNext():void {
			if (data.bytesAvailable == 0) {
				data.position = 0;
			}
			var len:int = data.readInt();
			var cell:ByteArray = new ByteArray();
			data.readBytes(cell, 0, len);
			cell.uncompress();
			trace("pixel " + cell.length/4);
			var index:int = 0;
			var i:int; var j:int;
			var z:Number;
			var vcolor:Array
			for (j = 0; j < cellHeight; j++) {
				for (i = 0; i < cellWidth; i++) {
					index+=2;
					z = cell.readFloat();
					_vertices[index++] = z;
					vcolor = getColor(z);
					_vertices[index++] = Number(vcolor[0])/255;
					_vertices[index++] = Number(vcolor[1])/255;
					_vertices[index++] = Number(vcolor[2])/255;
//					_vertices[index++] = 0;
//					_vertices[index++] = 1;
//					_vertices[index++] = 0;
				}
			}
			trace(_vertices.length);
			vertexbuffer.uploadFromVector(_vertices, 0, cellWidth*cellHeight);
		}
		
		public function getColor(z:Number):Array {
			if (z == 0) return colorNone;
			for (var i:int = 0; i < scales.length; i++) {
				if (z < scales[i]) return colors[i];
			}
			return colors[i];
		}
		
		protected function initMolehill(e:Event):void
		{
			data = loader.data;
			initVerices();
			
			context3D = stage.stage3Ds[0].context3D;			
			context3D.configureBackBuffer(1000, 1000, 1, true);
			
			// Create VertexBuffer3D. 3 vertices, of 6 Numbers each
			vertexbuffer = context3D.createVertexBuffer(cellWidth*cellHeight, 6);
			
//			vertices = Vector.<Number>([
//				-0.5, -0.5, 0, 1, 0, 0, // x, y, z, r, g, b
//				-0.5, 0.5, 0, 0, 1, 0,
//				0.5, 0.5, 0, 0, 0, 1]);
			
			//			vertexbuffer.dispose();
			// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
//			vertexbuffer.uploadFromVector(vertices, 0, 3);
			
//			var indices:Vector.<uint> = Vector.<uint>([0, 1, 2]);
			
			// Create IndexBuffer3D. Total of 3 indices. 1 triangle of 3 vertices
			indexbuffer = context3D.createIndexBuffer(_indices.length);			
			// Upload IndexBuffer3D to GPU. Offset 0, count 3
			indexbuffer.uploadFromVector (_indices, 0, _indices.length);			
			
			var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
			vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
				"m44 op, va0, vc0\n" + // pos to clipspace
//				"mov op, va0\n" + // pos to clipspace
				"mov v0, va1" // copy color
			);			
			
			var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
			fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
				
				"mov oc, v0"
			);
			
			program = context3D.createProgram();
			program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
			
			timer.addEventListener(TimerEvent.TIMER, onTimer);
//			timer.start();
			
			showNext();
			
			onTimer(null);
		}	
		
		private function onTimer(e:TimerEvent):void {
			trace("change points");
			context3D.clear ( 0, 0, 0, 0 );
			
//			vertices = Vector.<Number>([
//				-Math.random(),-Math.random(),0, 1, 0, 0, // x, y, z, r, g, b
//				-0.3, 0.3, 0, 0, 1, 0,
//				0.3, 0.3, 0, 0, 0, 1]);
			
//			vertexbuffer.dispose();
			// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
//			vertexbuffer.uploadFromVector(vertices, 0, 3);
			
			// vertex position to attribute register 0
			context3D.setVertexBufferAt(0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
			// color to attribute register 1
			context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
			// assign shader program
			context3D.setProgram(program);
			
			var m:Matrix3D = new Matrix3D();
//			m.appendRotation(-45, Vector3D.X_AXIS);
			m.appendTranslation(0, 0, 1);

			
			context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
			
			context3D.drawTriangles(indexbuffer);
			
			context3D.present();
		}
		
		protected function onRender(e:Event):void
		{
			if ( !context3D ) 
				return;
			
			context3D.clear ( 0, 0, 0, 0 );
			
			// vertex position to attribute register 0
			context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
			// color to attribute register 1
			context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
			// assign shader program
			context3D.setProgram(program);
			
			var m:Matrix3D = new Matrix3D();
			m.appendTranslation(0, 0, -1);
//			m.appendRotation(getTimer()/40, Vector3D.Z_AXIS);
			context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
			
			context3D.drawTriangles(indexbuffer);
			
			context3D.present();			
			
		}
		
	}
}